home *** CD-ROM | disk | FTP | other *** search
- /*
- * ppp.c - ppp support routines
- *
- * Copyright 1991-1992 William Allen Simpson
- * Licensed for non-profit non-commercial distribution
- * in Merit's PPP LAP for MacTCP
- *
- * Copyright 1992-1993 Merit Network, Inc. and The Regents of the
- * University of Michigan. Usage of this source code is restricted
- * to non-profit, non-commercial purposes. The source is provided
- * "as-is", without warranty.
- *
- * This code has been derived solely from the Jan 1991 public
- * domain release of PPP included in Phil Karn's KA9Q.
- */
- #include "ppp.h"
-
- /* Convert PPP header in host form to network form */
- void
- htonppp(LapInfo *lap, register b_16 protocol, struct bufheader *bufptr)
- {
- struct proto_s *proto_p = &(lap->lcp_i);
- register b_8 *cp;
- register long len;
- Boolean full_lcp, full_prot, full_acf;
-
- full_lcp = ( protocol == PPP_LCP_PROTOCOL );
- full_prot = ( full_lcp
- || !(proto_p->remote.work_negotiate & LCP_N_PFC)
- || protocol >= 0x00FF );
- full_acf = ( full_lcp
- || !(proto_p->remote.work_negotiate & LCP_N_ACFC) );
-
- len = full_acf * 2 + full_prot + 1;
-
- /* Prepend header onto packet data */
- bufptr->length += len;
- cp = (bufptr->dataptr -= len);
-
- /* Load header with proper values */
- if ( full_acf ) {
- *cp++ = HDLC_ALL_ADDR;
- *cp++ = HDLC_UI;
- }
- if ( full_prot )
- *cp++ = protocol >> 8;
-
- *cp++ = protocol;
- }
-
- /* Check for PPP Network-Layer Protocol Phase */
- void ppp_ready(LapInfo *lap)
- {
- if ( !(lap->ppp_flags & PPP_AP_REMOTE) ) {
- /* no pending authentication */
- lap->ppp_phase = pppNETWORK;
- lap->ppp_upsince = TickCount();
- fsm_up( &(lap->ppp_fsm[IPcp]) ); /* send an UP event to IPcp */
- }
- }
-
- void
- ppp_init (register LapInfo *lap )
- {
- register struct fsm_s *fsm_p;
-
- lap->ppp_phase = pppDEAD;
-
- /* init LCP */
- fsm_p = &(lap->ppp_fsm[Lcp]);
- fsm_p->pdc.protocol = PPP_LCP_PROTOCOL;
- fsm_p->pdc.option_limit = LCP_OPTION_LIMIT;
- fsm_p->pdc.option_lengths = lap->lcp_option_length_p;
- fsm_p->pdc.fsmi = Lcp;
- fsm_p->pdc.try_nak = LCP_NAK_TRY;
- fsm_p->pdc.try_terminate = LCP_TERM_TRY;
- fsm_p->lap = lap;
- fsm_p->pdv = &(lap->lcp_i);
- fsm_init(fsm_p);
-
- /* init IPCP */
- fsm_p = &(lap->ppp_fsm[IPcp]);
- fsm_p->pdc.protocol = PPP_IPCP_PROTOCOL;
- fsm_p->pdc.fsmi = IPcp;
- fsm_p->pdc.try_nak = IPCP_NAK_TRY;
- fsm_p->pdc.try_terminate = IPCP_TERM_TRY;
- fsm_p->pdc.option_limit = IPCP_OPTION_LIMIT;
- fsm_p->pdc.option_lengths = lap->ipcp_option_length_p;
- fsm_p->lap = lap;
- fsm_p->pdv = &(lap->ipcp_i);
- fsm_init(fsm_p);
-
- /* init PAP */
- fsm_p = &(lap->ppp_fsm[Pap]);
- fsm_p->pdc.protocol = PPP_PAP_PROTOCOL;
- fsm_p->pdc.fsmi = Pap;
- fsm_p->lap = lap;
- fsm_p->pdv = &(lap->pap_i);
- fsm_p->state = fsmCLOSED;
- }
-
- /* Keep track of changes in I-O status */
- short
- ppp_iostatus(LapInfo *lap, short command)
- {
- switch ( command ) {
- case PARAM_UP:
- if ( lap->ppp_phase == pppDEAD ) {
- lap->ppp_phase = pppESTABLISH;
- }
- fsm_up( &(lap->ppp_fsm[Lcp]) );
- return 0;
-
- case PARAM_DOWN:
- fsm_down( &(lap->ppp_fsm[Lcp]) );
- lap->ppp_phase = pppDEAD;
- return 0;
- };
- return -1;
- }
-
- /* Process configuration ACK sent by remote host */
- short proc_ack(fsm_p, config, bufptr)
- struct fsm_s *fsm_p;
- struct config_hdr *config;
- struct bufheader *bufptr;
- {
- struct bufheader *req_buf;
-
- /* ID field must match last request we sent */
- if (config->id != fsm_p->lastid) {
- PPP_DEBUG_CHECKS("\pACK: wrong ID");
- goto bad_return;
- }
-
- /* Get a copy of last request we sent */
- if ( (req_buf = makereq(fsm_p)) == nil)
- goto bad_return;
-
- /* Overall buffer length should match */
- if (config->len != req_buf->length) {
- PPP_DEBUG_CHECKS("\pACK: buffer length mismatch");
- goto bad_return2;
- } else {
- register short req_char;
- register short ack_char;
-
- /* Each byte should match */
- while ((req_char = yankbyte(req_buf)) != -1) {
- if ((ack_char = yankbyte(bufptr)) == -1
- || ack_char != req_char ) {
- PPP_DEBUG_CHECKS("\pACK: data mismatch");
- goto bad_return2;
- }
- }
- }
- release(req_buf);
- release(bufptr);
- return 0;
-
- bad_return2:
- release(req_buf);
- bad_return:
- release(bufptr);
- return -1;
- }
-
- /************************************************************************/
- /* Process configuration NAK sent by remote host */
- short proc_nak(fsm_p, config, bufptr)
- struct fsm_s *fsm_p;
- struct config_hdr *config;
- struct bufheader *bufptr;
- {
- struct proto_s *proto_p = (struct proto_s *) (fsm_p->pdv);
- long signed_length = config->len;
- struct option_hdr option;
- short last_option = 0;
- short result;
-
- /* ID field must match last request we sent */
- if (config->id != fsm_p->lastid) {
- PPP_DEBUG_CHECKS("\pNAK: wrong ID");
- goto bad_return;
- }
-
- /* First, process in order. Then, process extra "important" options */
- while (signed_length > 0 && ntohopt(&option, bufptr) != -1) {
- if ((signed_length -= option.len) < 0) {
- PPP_DEBUG_CHECKS("\pNAK: bad header length");
- goto bad_return;
- }
- if ( option.type > fsm_p->pdc.option_limit ) {
- PPP_DEBUG_CHECKS("\pNAK: option out of range");
- } else if ( option.type < last_option
- || !(proto_p->local.work_negotiate & (1 << option.type)) ) {
- if (proto_p->local.work_negotiate & (1 << option.type)) {
- PPP_DEBUG_CHECKS("\pNAK: option out of order");
- goto bad_return;
- }
- proto_p->local.work_negotiate |= (1 << option.type);
- last_option = fsm_p->pdc.option_limit + 1;
- } else {
- last_option = option.type;
- }
- if ( ( result = option_check( bufptr, fsm_p, &option, FALSE ) ) == -1 ) {
- PPP_DEBUG_CHECKS("\pNAK: ran out of data");
- goto bad_return;
- }
- /* update the negotiation status */
- if ( result == CONFIG_REJ
- && option.type <= fsm_p->pdc.option_limit ) {
- proto_p->local.work_negotiate &= ~(1 << option.type);
- }
- }
- release(bufptr);
- return 0;
-
- bad_return:
- release(bufptr);
- return -1;
- }
-
- /* Process configuration reject sent by remote host */
- short
- proc_reject(fsm_p, config, bufptr)
- struct fsm_s *fsm_p;
- struct config_hdr *config;
- struct bufheader *bufptr;
- {
- struct proto_s *proto_p = (struct proto_s *) (fsm_p->pdv);
- long signed_length = config->len;
- struct option_hdr option;
- short last_option = 0;
-
- /* ID field must match last request we sent */
- if (config->id != fsm_p->lastid) {
- PPP_DEBUG_CHECKS("\pREJ: wrong ID");
- goto bad_return;
- }
-
- /* Process in order, checking for errors */
- while (signed_length > 0 && ntohopt(&option, bufptr) != -1) {
- register short k;
-
- if ((signed_length -= option.len) < 0) {
- PPP_DEBUG_CHECKS("\pREJ: bad header length");
- goto bad_return;
- }
- if ( option.type > fsm_p->pdc.option_limit ) {
- PPP_DEBUG_CHECKS("\pIPCP REJ: option out of range");
- } else if (option.type < last_option
- || !(proto_p->local.work_negotiate & (1 << option.type))) {
- PPP_DEBUG_CHECKS("\pREJ: option out of order");
- goto bad_return;
- }
- for ( k = option.len - OPTION_HDR_LEN; k-- > 0; ) {
- if ( yankbyte(bufptr) == -1 ) {
- PPP_DEBUG_CHECKS("\pREJ: ran out of data");
- goto bad_return;
- }
- }
-
- if ( (last_option = option.type) <= fsm_p->pdc.option_limit) {
- proto_p->local.work_negotiate &= ~(1 << option.type);
- if (fsm_p->pdc.fsmi == IPcp &&
- option.type == IPCP_ADDRESS) { /* try old style address negotiationg */
- proto_p->local.work_negotiate |= IPCP_N_ADDRESSES;
- if (proto_p->remote.want_negotiate & IPCP_N_ADDRESS)
- proto_p->remote.want_negotiate ^=
- IPCP_N_ADDRESS | IPCP_N_ADDRESSES;
- }
- }
- }
- release(bufptr);
- return 0;
-
- bad_return:
- release(bufptr);
- return -1;
- }
-
- /* Check the options, updating the working values.
- * Returns -1 if ran out of data, ACK/NAK/REJ as appropriate.
- */
- short
- option_check(struct bufheader *bufptr, struct fsm_s *fsm_p,
- struct option_hdr *option_p, short request)
- {
- short toss = option_p->len - OPTION_HDR_LEN;
- register int option_result = CONFIG_ACK; /* Assume good values */
- short test;
- register long temp_val;
- register b_8 optype = option_p->type;
- b_8 *cp = bufptr->dataptr;
- struct proto_s *proto_p = ( (struct proto_s *) (fsm_p->pdv) );
- struct option_s *side_p;
-
- if (request)
- side_p = &(proto_p->remote);
- else
- side_p = &(proto_p->local);
-
- switch (fsm_p->pdc.fsmi) {
- case Lcp: {
-
- if ( !(side_p->will_negotiate & (1 << optype))) {
- option_result = CONFIG_REJ; /* if we aren't willing to negotiate */
- break;
- }
-
- switch(optype) {
- case LCP_MRU:
- side_p->work.lcp_option.mru = yank16(bufptr);
- toss -= 2;
-
- /* Check if new value is appropriate */
- if (side_p->work.lcp_option.mru < LCP_MRU_LO) {
- side_p->work.lcp_option.mru = LCP_MRU_LO;
- option_result = CONFIG_NAK;
- } else if (side_p->work.lcp_option.mru > LCP_MRU_HI) {
- side_p->work.lcp_option.mru = LCP_MRU_HI;
- option_result = CONFIG_NAK;
- }
- if ( (side_p->want_negotiate & LCP_N_MRU) && !request
- && side_p->work.lcp_option.mru > side_p->want.lcp_option.mru ) {
- side_p->work.lcp_option.mru = side_p->want.lcp_option.mru;
- option_result = CONFIG_NAK;
- }
- break;
-
- case LCP_ACCM:
- side_p->work.lcp_option.accm = yank32(bufptr);
- toss -= 4;
- /* Remote host may ask to escape more control */
- /* characters than we require, but must escape */
- /* at least the control chars that we require. */
- if ( (!request || (side_p->want_negotiate & LCP_N_ACCM))
- && side_p->work.lcp_option.accm !=
- (side_p->work.lcp_option.accm | side_p->want.lcp_option.accm) ) {
- side_p->work.lcp_option.accm |= side_p->want.lcp_option.accm;
- option_result = CONFIG_NAK;
- }
- break;
-
- case LCP_AUTHENT:
- side_p->work.lcp_option.authentication = yank16(bufptr);
- toss -= 2;
-
- /* Check if new value is appropriate */
- switch ( side_p->work.lcp_option.authentication ) {
- case PPP_PAP_PROTOCOL:
- /* Yes */
- break;
- default:
- option_p->len = 4; /* length of PAP option */
- side_p->work.lcp_option.authentication = PPP_PAP_PROTOCOL;
- option_result = CONFIG_NAK;
- break;
- };
- break;
-
- case LCP_MAGIC:
- side_p->work.lcp_option.magic_number = yank32(bufptr);
- toss -= 4;
-
- /* Ensure that magic numbers are different */
- if (side_p->work.lcp_option.magic_number == 0L
- || proto_p->remote.work.lcp_option.magic_number == \
- proto_p->local.work.lcp_option.magic_number) {
- side_p->work.lcp_option.magic_number += TickCount() * (long) bufptr;
- option_result = CONFIG_NAK;
- }
- break;
-
- case LCP_PFC:
- break;
-
- case LCP_ACFC:
- break;
-
- default:
- option_result = CONFIG_REJ;
- }
- }
- break;
- case IPcp: {
-
- if ( !(side_p->will_negotiate & (1 << optype))) {
- option_result = CONFIG_REJ; /* if we aren't willing to negotiate */
- break;
- }
-
- switch(optype) {
- case IPCP_ADDRESSES:
- case IPCP_ADDRESS:
- side_p->work.ipcp_option.address = get32(cp); /* get address */
- if (optype == IPCP_ADDRESSES)
- side_p->work.ipcp_option.other = get32(cp+4); /* if addresses, get other address */
- if ( !request ) {
- /* override any undesirable changes */
- if (proto_p->remote.want.ipcp_option.address != 0L) {
- proto_p->local.work.ipcp_option.other
- = proto_p->remote.want.ipcp_option.address;
- }
-
- if (proto_p->local.want.ipcp_option.address != 0L) {
- proto_p->local.work.ipcp_option.address
- = proto_p->local.want.ipcp_option.address;
- }
- break;
- }
-
- /* Ensure that addresses match */
- if (proto_p->remote.work.ipcp_option.address ==
- proto_p->remote.want.ipcp_option.address) {
- if (proto_p->remote.want.ipcp_option.address == 0L) {
- /* don't know address either */
- option_result = CONFIG_REJ;
- }
- } else if (proto_p->remote.want.ipcp_option.address == 0L) {
- proto_p->local.work.ipcp_option.other =
- proto_p->remote.work.ipcp_option.address;
- } else {
- proto_p->remote.work.ipcp_option.address =
- proto_p->remote.want.ipcp_option.address;
- option_result = CONFIG_NAK;
- }
-
- if (optype == IPCP_ADDRESSES) {
- if (proto_p->remote.work.ipcp_option.other ==
- proto_p->local.want.ipcp_option.address) {
- if (proto_p->local.want.ipcp_option.address == 0L) {
- /* don't know address either */
- option_result = CONFIG_REJ;
- }
- } else if (proto_p->local.want.ipcp_option.address == 0L) {
- proto_p->local.work.ipcp_option.address =
- proto_p->remote.work.ipcp_option.other;
- } else {
- option_result = CONFIG_NAK;
- proto_p->remote.work.ipcp_option.other =
- proto_p->local.want.ipcp_option.address;
- }
- }
- break;
-
- case IPCP_COMPRESS:
- side_p->work.ipcp_option.compression = yank16(bufptr);
- toss -= 2;
- /* Check if requested type is acceptable */
- switch ( side_p->work.ipcp_option.compression ) {
- case PPP_COMPR_PROTOCOL:
- if ( toss == 0 ) { /* check if old-style VJ option */
- option_result = CONFIG_REJ;
- break;
- }
- if ( (test = yankbyte(bufptr)) == -1 )
- return -1;
- if ( (side_p->work.ipcp_option.slots = test + 1) < IPCP_SLOT_LO) {
- side_p->work.ipcp_option.slots = IPCP_SLOT_LO;
- option_result = CONFIG_NAK;
- } else if (side_p->work.ipcp_option.slots > IPCP_SLOT_HI && !request) {
- side_p->work.ipcp_option.slots = IPCP_SLOT_HI;
- option_result = CONFIG_NAK;
- }
-
- if ( (test = yankbyte(bufptr)) == -1 )
- return -1;
- if ( (side_p->work.ipcp_option.slot_compress = test) > 1 ) {
- side_p->work.ipcp_option.slot_compress = 1;
- option_result = CONFIG_NAK;
- }
- toss -= 2;
- break;
-
- default:
- if ( side_p->want_negotiate & IPCP_N_COMPRESS ) {
- side_p->work.ipcp_option.compression = side_p->want.ipcp_option.compression;
- side_p->work.ipcp_option.slots = side_p->want.ipcp_option.slots;
- side_p->work.ipcp_option.slot_compress = side_p->want.ipcp_option.slot_compress;
- } else {
- side_p->work.ipcp_option.compression = PPP_COMPR_PROTOCOL;
- side_p->work.ipcp_option.slots = IPCP_SLOT_DEFAULT;
- side_p->work.ipcp_option.slot_compress = IPCP_SLOT_COMPRESS;
- }
- option_result = CONFIG_NAK;
- break;
- };
- break;
-
- default:
- option_result = CONFIG_REJ;
- break;
- }
- }
- }
- if ( toss < 0 )
- return -1;
-
- if ( !request || (option_result != CONFIG_REJ)) {
- /* toss extra bytes in option */
- while( toss-- > 0 ) {
- if ( yankbyte(bufptr) == -1 )
- return -1;
- }
- }
- return (option_result);
- }
-
- /* Check options requested by the remote host */
- short proc_request(fsm_p, config, bufptr)
- struct fsm_s *fsm_p;
- struct config_hdr *config;
- struct bufheader *bufptr;
- {
- long signed_length = config->len;
- struct bufheader *reply_buf; /* reply packet */
- short reply_result = CONFIG_ACK; /* reply to request */
- b_16 desired; /* desired to negotiate */
- struct option_hdr option; /* option header storage */
- short option_result; /* option reply */
- struct bufheader *rejdata; /* for CONFIG rejects to return data */
- struct proto_s *proto_p = (struct proto_s *) (fsm_p->pdv);
-
- proto_p->remote.work_negotiate = FALSE; /* clear flags */
-
- if ((reply_buf = getbuffer()) == nil)
- goto bad_return2;
-
- /* Process options requested by remote host */
- while (signed_length > 0 && ntohopt(&option, bufptr) != -1) {
- if ((signed_length -= option.len) < 0) {
- PPP_DEBUG_CHECKS("\pREQ: bad header length");
- goto bad_return;
- }
-
- if ( ( option_result = option_check(bufptr, fsm_p,
- &option, TRUE ) ) == -1 ) {
- PPP_DEBUG_CHECKS("\pREQ: ran out of data");
- goto bad_return;
- }
-
- if ( option_result < reply_result ) {
- continue;
- } else if ( option_result > reply_result ) {
- /* Discard current list of replies */
- release(reply_buf);
- reply_buf = getbuffer();
- reply_result = option_result;
- }
-
- /* remember that we processed option */
- if ( option_result != CONFIG_REJ ) {
- proto_p->remote.work_negotiate |= (1 << option.type);
- rejdata = nil;
- } else {
- rejdata = bufptr;
- }
-
- /* Add option response to the return list */
- add_option(fsm_p, reply_buf, &(proto_p->remote.work), option.type, option.len, rejdata);
- }
-
- /* Now check for any missing options which are desired */
- if ( fsm_p->retry_nak > 0
- && (desired = proto_p->remote.want_negotiate &
- ~proto_p->remote.work_negotiate) != 0 ) {
- switch ( reply_result ) {
- case CONFIG_ACK:
- release(reply_buf);
- reply_buf = getbuffer();
- reply_result = CONFIG_NAK;
- /* fallthru */
- case CONFIG_NAK:
- makeoptions(fsm_p, reply_buf, &(proto_p->remote.want), desired );
- fsm_p->retry_nak--;
- break;
- case CONFIG_REJ:
- /* do nothing */
- break;
- };
- } else if ( reply_result == CONFIG_NAK ) {
- /* if too many NAKs, reject instead */
- if ( fsm_p->retry_nak > 0 )
- fsm_p->retry_nak--;
- else
- reply_result = CONFIG_REJ;
- }
-
- /* Send ACK/NAK/REJ to remote host */
- fsm_send(fsm_p, reply_result, config->id, reply_buf);
- release(bufptr);
- return (reply_result != CONFIG_ACK);
-
- bad_return:
- release(reply_buf);
- bad_return2:
- release(bufptr);
- return -1;
- }
-
- /* Build a list of options */
- void makeoptions(struct fsm_s *fsm_p, struct bufheader *bufptr,
- union value_s *value_p, b_16 negotiating)
- {
- register short o_type;
-
- for ( o_type = 1; o_type <= fsm_p->pdc.option_limit ; o_type++ ) {
- if (negotiating & (1 << o_type)) {
- add_option(fsm_p, bufptr, value_p, o_type,
- *(fsm_p->pdc.option_lengths + o_type), nil);
- }
- }
- }
-
- /* add an option to a packet */
- void add_option(struct fsm_s *fsm_p, struct bufheader *bufptr,
- union value_s *value_p, b_8 o_type, b_8 o_length, struct bufheader *copy_buffer)
- {
- register b_8 *cp;
- register short copylen;
-
- cp = bufptr->dataptr + bufptr->length;
- *cp++ = o_type;
- *cp++ = o_length;
- bufptr->length += o_length;
-
- if (copy_buffer != nil) { /* if we should just copy the data */
- copylen = o_length - OPTION_HDR_LEN;
- while ( copylen-- > 0 ) {
- *cp++ = yankbyte(copy_buffer);
- }
- return;
- }
- if (fsm_p->pdc.fsmi == Lcp) { /* LCP Options */
- switch ( o_type ) {
- case LCP_MRU:
- put16(cp, value_p->lcp_option.mru);
- break;
-
- case LCP_ACCM:
- put32(cp, value_p->lcp_option.accm);
- break;
-
- case LCP_AUTHENT:
- put16(cp, value_p->lcp_option.authentication);
- break;
-
- case LCP_MAGIC:
- put32(cp, value_p->lcp_option.magic_number);
- break;
-
- case LCP_PFC:
- case LCP_ACFC:
- break;
-
- }
- } else { /* IPCP options */
- switch ( o_type ) {
- case IPCP_ADDRESSES:
- cp = put32(cp, value_p->ipcp_option.address);
- put32(cp, value_p->ipcp_option.other);
- break;
-
- case IPCP_ADDRESS:
- put32(cp, value_p->ipcp_option.address);
- break;
-
- case IPCP_COMPRESS:
- cp = put16(cp, value_p->ipcp_option.compression);
- if ( value_p->ipcp_option.compression == PPP_COMPR_PROTOCOL ) {
- *cp++ = value_p->ipcp_option.slots - 1;
- *cp++ = value_p->ipcp_option.slot_compress;
- }
- break;
- }
- }
- }
-
- /* Build a request to send to remote host */
- struct bufheader *
- makereq(struct fsm_s *fsm_p)
- {
- struct bufheader *req_buffer;
- struct proto_s *proto_p = (struct proto_s *) (fsm_p->pdv);
-
- if (fsm_p->pdc.fsmi == Pap) {
- struct pap_s *pap_p = fsm_p->pdv;
- register b_8 *cp;
- short len, userlen, passlen;
-
- if ( pap_p->username[0] == -1
- || pap_p->password[0] == -1 ) {
- PPP_DEBUG_CHECKS("\pNULL username or password");
- return nil;
- }
-
- userlen = pap_p->username[0];
- passlen = pap_p->password[0];
-
- len = 2 + userlen + passlen;
- if ((req_buffer = getbuffer()) == nil)
- return nil;
-
- /* Load user id and password for authenticate packet */
- cp = req_buffer->dataptr;
- *cp++ = userlen;
- if (userlen > 0) {
- BlockMove(&pap_p->username[1], cp, (long) userlen);
- cp += userlen;
- }
- *cp++= passlen;
- if ( passlen > 0 )
- BlockMove(&pap_p->password[1], cp, (long) passlen);
-
- req_buffer->length += len;
- return(req_buffer);
- }
-
- if ( (req_buffer = getbuffer()) != nil)
- makeoptions( fsm_p, req_buffer, &(proto_p->local.work),
- proto_p->local.work_negotiate );
- return(req_buffer);
- }
-